#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

#define N 5

std::vector<std::function<void()>> tasks;

class channel
{
public:
    channel(int cmdfd, int slavorid, std::string name)
    :_cmdfd(cmdfd)
    ,_slavorid(slavorid)
    ,_name(name)
    {}
public:
    int _cmdfd;         // 发送任务的文件描述符
    pid_t _slavorid;    // 子进程PID
    std::string _name;  // 子进程名称 
};

void slavor()
{
    while (true)
    {
        int cmdcode = 0;
        int n = read(0, &cmdcode, sizeof(int));
        if (n == sizeof(int))
        {
            //printf("child process receive, cmdcode: %d, pid: %d\n", cmdcode, getpid()); 
            if (cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        else if (n == 0) break;
        // sleep(1);
    }
}

void InitProcessPool(std::vector<channel>* channels)
{
    std::vector<int> oldfds;
    for (int i = 0; i < N; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd);    // 创建匿名管道
        assert(!n);
        (void)n;
        pid_t id = fork();
        if (id == 0)
        {
            for (auto& e : oldfds)
            {
                close(e);
            }
            dup2(pipefd[0], 0);  // 重定向，子进程从0读取。
            close(pipefd[0]);
            close(pipefd[1]);
 
            slavor();
            exit(0); 
        }
        close(pipefd[0]);
        std::string name = "process-" + std::to_string(i + 1);
        channels->push_back(channel(pipefd[1], id, name));
        oldfds.push_back(pipefd[1]);
    }
}

void Debug(const std::vector<channel>& channels)
{  
    for (const auto& iter : channels)
    {
        printf("create a process, cmdfd: %d, slavorid: %d, name: %s\n",
        iter._cmdfd, iter._slavorid, iter._name.c_str());
    }
}

void Menu()
{
    std::cout << "*******************************************************" << std::endl;
    std::cout << "****************1.刷新日志   2.刷新野怪*****************" << std::endl;
    std::cout << "****************3.检测更新   4.释放技能*****************" << std::endl;
    std::cout << "*********************** 0.退出 ************************" << std::endl;
    std::cout << "*******************************************************" << std::endl;
}

void CtrlProcess(const std::vector<channel>& channels)
{
    int num = 0;
    while (true)
    {
        Menu();
        int select = 0;
        std::cout << "Please Enter@ ";
        std::cin >> select;
        if (select <= 0 || select > tasks.size())  break;

        int cmdcode = select - 1;

        //int cmdcode = rand() % 20;
        // int cmdcode = rand() % tasks.size();
        //int n = rand() % 5;
        int n = num;
        num++;
        num %= channels.size();
        write(channels[n]._cmdfd, &cmdcode, sizeof(int));

        //printf("father process send, cmdcode: %d, slavorid: %d, name: %s\n",
        //cmdcode, channels[n]._slavorid, channels[n]._name.c_str());
        // sleep(1);
    }

}

void QuitProcess(const std::vector<channel>& channels)
{
    for (const auto& e : channels)
    {
        close(e._cmdfd);
        waitpid(e._slavorid, nullptr, 0);
    }

    // for (const auto& e : channels)
    //     close(e._cmdfd);
    // for (const auto& e : channels)
    // {
    //     waitpid(e._slavorid, nullptr, 0);
    // }
}

int main()
{
    srand(time(nullptr)^getpid()^1023);  // 种一颗随机数种子
    std::vector<channel> channels;
    InitTasks(&tasks);
    InitProcessPool(&channels);
    //Debug(channels);
    CtrlProcess(channels);
    
    QuitProcess(channels);
    return 0;
}